package cli

import (
	"fmt"
	"github.com/hashicorp/hcl/v2/hclsimple"
	"github.com/ihaiker/tfwrapper/internal/config"
	"github.com/ihaiker/tfwrapper/internal/logger"
	"github.com/ihaiker/tfwrapper/internal/util"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli/v2"
	"os"
	"path"
	"path/filepath"
	"runtime"
)

var flags = new(config.Flags)
var parsed = false

func init() {
	flags.LogLevel = "warn"
}

const (
	flagCliConfig       = "tfwrapper-cli-config"
	flagDownloadDir     = "tfwrapper-download-dir"
	flagParallelism     = "tfwrapper-parallelism"
	flagNonInteractive  = "tfwrapper-non-interactive"
	flagConfigName      = "tfwrapper-config-name"
	flagLogLevel        = "tfwrapper-log-level"
	flagStack           = "tfwrapper-stack"
	flagTerraformBinary = "tfwrapper-terraform-binary"
)

var app = &cli.App{
	EnableBashCompletion: true,
	HideHelp:             false, HideVersion: false, Suggest: true,
	DefaultCommand: "run",
	Version:        "0.0.1",
	Authors: []*cli.Author{
		{
			Name:  "haiker zhou",
			Email: "ni@renzhen.la",
		},
	},
	Flags: []cli.Flag{
		&cli.PathFlag{Name: flagCliConfig, EnvVars: []string{util.ToEnvKey(flagCliConfig)},
			Usage: "The tfwrapper command cli config file."},
		&cli.PathFlag{Name: flagDownloadDir, EnvVars: []string{util.ToEnvKey(flagDownloadDir)},
			Usage: "Download dir, default is the config file current dir."},
		&cli.UintFlag{Name: flagParallelism, EnvVars: []string{util.ToEnvKey(flagParallelism)},
			Usage: "Set to at most N modules"},
		&cli.StringFlag{Name: flagTerraformBinary, EnvVars: []string{util.ToEnvKey(flagTerraformBinary)},
			Usage: "The path of terraform binary, if not set, will use default."},
		&cli.StringFlag{Name: flagConfigName, EnvVars: []string{util.ToEnvKey(flagConfigName)},
			Usage: "The name of config file.", DefaultText: config.WrapperConfigFile},
		&cli.StringFlag{Name: flagLogLevel, EnvVars: []string{util.ToEnvKey(flagLogLevel)},
			Usage: "Set the logging level. One of: debug|info|warn|error", DefaultText: "warn"},
		&cli.StringFlag{Name: flagStack, EnvVars: []string{util.ToEnvKey(flagStack)}, Usage: "The stack name take effect."},
		&cli.BoolFlag{Name: flagNonInteractive, EnvVars: []string{util.ToEnvKey(flagNonInteractive)},
			Usage: "Whether we should prompt the user for confirmation or always assume \"yes\""},
	},
	Before: func(ctx *cli.Context) error {
		if parsed {
			return nil
		}
		parsed = true

		err := preLoadConfiguration(ctx)
		if err != nil {
			return err
		}

		if ctx.IsSet(flagDownloadDir) {
			absPath := ctx.String(flagDownloadDir)
			if !path.IsAbs(absPath) {
				if absPath, err = filepath.Abs(absPath); err != nil {
					return err
				} else if !util.Exists(absPath) || util.IsFile(absPath) {
					if err = os.MkdirAll(absPath, 0755); err != nil {
						return fmt.Errorf("download dir not exists: %s", absPath)
					}
				}
			}
			flags.DownloadDir = absPath
		}
		if ctx.IsSet(flagParallelism) {
			flags.Parallelism = util.UintPtr(ctx.Uint(flagParallelism))
		}
		if ctx.IsSet(flagTerraformBinary) {
			flags.TerraformBinary = ctx.String(flagTerraformBinary)
		}
		if ctx.IsSet(flagConfigName) {
			flags.ConfigName = ctx.String(flagConfigName)
		}
		if ctx.IsSet(flagLogLevel) {
			flags.LogLevel = ctx.String(flagLogLevel)
		}
		if ctx.IsSet(flagStack) {
			flags.Stack = ctx.String(flagStack)
		}
		if ctx.IsSet(flagNonInteractive) {
			flags.NonInteractive = util.BoolPtr(ctx.Bool(flagNonInteractive))
		}
		return setLogger()
	},
	Action: func(ctx *cli.Context) error {
		os.Args = append([]string{os.Args[0], "run"}, ctx.Args().Slice()...)
		return ctx.App.RunContext(ctx.Context, os.Args)
	},
}

func preLoadConfiguration(ctx *cli.Context) (err error) {
	flags.CliConfigFile = ctx.String("tfwrapper-cli-config")
	if flags.CliConfigFile == "" { //search default config
		if defaultConfig := os.ExpandEnv("$HOME/.tfwrapper/config.hcl"); util.Exists(defaultConfig) {
			flags.CliConfigFile = defaultConfig
		}
	}
	if flags.CliConfigFile == "" {
		return
	}
	if !util.Exists(flags.CliConfigFile) {
		return fmt.Errorf("cli config file not exists: %s", flags.CliConfigFile)
	}
	return hclsimple.DecodeFile(flags.CliConfigFile, nil, flags)
}

func setLogger() error {
	level, err := logrus.ParseLevel(flags.LogLevel)
	if err == nil {
		logrus.SetLevel(level)
	}
	return err
}

// Execute executes Cobra commands.
func Execute(version, commitLog, buildDate string) error {
	logger.SetLogger()

	app.Version = version
	cli.VersionPrinter = func(cCtx *cli.Context) {
		fmt.Printf("tfwrapper version: %s, git commit : %s, build time: %s, go: %s",
			cCtx.App.Version, commitLog, buildDate, runtime.Version())
	}
	cli.AppHelpTemplate += fmt.Sprintf("\nBuild Time: %s, Git commit ID: %s\n", buildDate, commitLog)
	return app.Run(os.Args)
}
